<?php
/*
 * Glorious Framework
 *
 * Form Validator class
 *
 * (c) 2007-2010 Konspire Design
 *
 * $Id: form_validator.php,v f261dc61ec42 2010/05/06 12:40:33 and $
 * $Author: and $
 * $Date: 2010/05/06 12:40:33 $
*/

load_helper('form');

class Form_validator {

	protected $nonce_action = '';
	protected $error_format = '<p class="error">%s</p>';
	protected $validator_errors = array();
	protected $errors = array();
	protected $rules = array();
	protected $allowed_php_filters = array(
			'trim', 'ltrim', 'rtrim',
			'intval', 'abs', 'floatval', 'doubleval');

	public function __construct() {
		$this->validator_errors = array();

		$this->validator_errors['required'] = __('The :label field is required.');
		$this->validator_errors['matches'] = __('The :field1 field does not match the :field2 field.');
		$this->validator_errors['min_length'] = __('The :label field must be at least :len characters in length.');
		$this->validator_errors['max_length'] = __('The :label field must be not more than :len characters in length.');
		$this->validator_errors['exact_length'] = __('The :label field must be exactly :len characters in length.');
		$this->validator_errors['regexp'] = __('The :label field has invalid format or contains restricted characters.');
		$this->validator_errors['valid_email'] = __('The :label field contains not well formed E-mail address.');
		$this->validator_errors['valid_emails'] = __('The :label field contains not well formed comma-separated list of E-mail addresses.');
		$this->validator_errors['valid_base64'] = __('The :label field contains invalid base64 data.');
		$this->validator_errors['valid_ip'] = __('The :label field contains invalid IP address.');
	}

	public function reset() {
		$this->rules = array();
		$this->errors = array();
	}

	public function set_error_format($format) {
		$this->error_format = $format;
	}

	public function set_error_string($field_name, $error) {
		$this->error_string[$field_name] = $error;
	}

	public function format_errors($format = '', $field = '') {
		if(empty($format))
			$format = $this->error_format;

		$str = '';

		if($field === '') {
			foreach($this->errors as $errfield => $errstr)
				$str .= sprintf($this->error_format, $errstr) . "\n";
		} else {
			if(isset($this->errors[$field]))
				$str = sprintf($this->error_format, $this->errors[$field]);
		}

		return $str;
	}

	public function set_error($field_name, $error) {
		if(!empty($field_name))
			$this->errors[$field_name] = $error;
		else
			$this->errors[] = $error;
	}

	public function has_error($field_name) {
		return isset($this->errors[$field_name]);
	}

	public function get_error($field_name) {
		if($this->has_error($field_name))
			return $this->errors[$field_name];

		return false;
	}

	public function set_rules($field_name, $field_label, $rules) {
		$this->rules[$field_name] = array(
				'label' => $field_label,
				'rules' => $rules);
	}

	public function get_rules($field_name = '') {
		if($field_name === '')
			return $this->rules;
		else if(isset($field_name))
			return $this->rules[$field_name];

		return false;
	}

	public function get_label($field_name) {
		if(isset($this->rules[$field_name]))
			return $this->rules[$field_name]['label'];

		return false;
	}

	public function replace_error($pattern, $fields) {
		return str_replace(array_map( create_function( '$a', 'return ":" . $a;' ), array_keys($fields)), array_values($fields), $pattern);
	}

	public function validator_error($validator, $fields = array()) {
		$pattern = '';
		
		if(isset($this->validator_errors[$validator]))
			$pattern = $this->validator_errors[$validator];
		
		if(empty($fields))
			return $pattern;

		return $this->replace_error($pattern, $fields);
	}

	public function set_validator_error($validator, $str) {
		$this->validator_errors[$validator] = $str;
	}

	protected function normalize_attr($str) {
		return str_replace(array('-', '['), '_', preg_replace( '#[^a-z0-9_\[-]+#i', '', $str));
	}

	protected function get_real_fieldname($field_name) {
		$form_prefix = $this->normalize_attr($this->nonce_action);
		if($form_prefix != '')
			return preg_replace('#^(\w+)(.*?)$#i', preg_quote($form_prefix) . '[$1]$2', $field_name);

		return $field_name;
	}

	public function run($action = '') {

		$this->nonce_action = $action;

		if(!empty($action)) {
			$post_action = Request::instance()->req('action');

			if($post_action != $action)
				return false;

			if(!Nonce::instance()->check_nonce($action)) {
				$this->errors[] = __('Invalid nonce, try again?');
				return false;
			}
		}

		foreach($this->rules as $field_name => $item) {
			// POST value passed by ref so it will be changed if you've changed it
			$real_field_name = $this->get_real_fieldname($field_name);

			$field_value =& array_traverse($real_field_name, $_POST, null);

			foreach($item['rules'] as $k => $v) {
				// Process key/value pairs as rule/value for rule
				// or if the key is just number so no value provided

				$call_func = null;
				$call_arg = null;

				if(is_int($k)) {
					// callback
					if(is_array($v) && isset($v['callback'])) {
						$call_func = $v['callback'];
						$v['validator'] =& $this;
						unset($v['callback']);
						$call_arg = $v;
					} elseif(in_array($v, $this->allowed_php_filters)) {
						// check for php filter functions
						$field_value = $v($field_value);
						continue;
					} else {
						// validators without argument expected
						$call_func = array($this, $v);
						$call_arg = null;
					}
				}
				else {
					// any validators with argument expected
					$call_func = array($this, $k);
					$call_arg = $v;
				}

				if(is_callable($call_func) &&
						!call_user_func($call_func, $field_name, $field_value, $call_arg))
					break 1;
			}
		}

		if(!sizeof($this->errors))
			return true;

		return false;
	}

	/*
     * Internal validation functions
	*/

	public function required($field_name, $field_value) {
		if(!is_array($field_value))
			$field_value = trim($field_value);

		if(is_array($field_value) && !empty($field_value))
			return true;
		else if($field_value != '')
			return true;

		$label = $this->get_label($field_name);
		$this->set_error($field_name, $this->validator_error('required', compact('label')));

		return false;
	}

	public function matches($field_name, $field_value, $field_name2) {

		$real_field_name2 = $this->get_real_fieldname($field_name2);
		
		$result = array_traverse($real_field_name2, $_POST, null);

		if(!is_null($result) && $result == $field_value)
			return true;

		$field1 = $this->get_label($field_name);
		$field2 = $this->get_label($field_name2);

		$this->set_error($field_name, $this->validator_error('matches', compact('field1', 'field2')));

		return false;
	}

	public function min_length($field_name, $field_value, $len) {

		if(mb_strlen($field_value) >= $len)
			return true;

		$label = $this->get_label($field_name);

		$this->set_error($field_name, $this->validator_error('min_length', compact('label', 'len')));

		return false;
	}

	public function max_length($field_name, $field_value, $len) {
		if(mb_strlen($field_value) <= $len)
			return true;

		$label = $this->get_label($field_name);

		$this->set_error($field_name, $this->validator_error('max_length', compact('label', 'len')));

		return false;
	}

	public function exact_length($field_name, $field_value, $len) {
		if(mb_strlen($field_value) == $len)
			return true;

		$label = $this->get_label($field_name);

		$this->set_error($field_name, $this->validator_error('min_length', compact('label', 'len')));

		return false;
	}

	public function regexp($field_name, $field_value, $regexp) {
		if(preg_match($regexp, $field_value))
			return true;

		$label = $this->get_label($field_name);

		$this->set_error($field_name, $this->validator_error('regexp', compact('label')));

		return false;
	}

	public function is_valid_email($email) {
		return preg_match("/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/ix", $email);
	}

	public function valid_email($field_name, $field_value) {
		if($this->is_valid_email($field_value))
			return true;

		$label = $this->get_label($field_name);

		$this->set_error($field_name, $this->validator_error('valid_email', compact('label')));

		return false;
	}

	public function valid_emails($field_name, $field_value) {
		if (strpos($field_value, ',') === false)
			return $this->valid_email($field_name, trim($field_value));

		foreach(explode(',', $field_value) as $email) {
			if (trim($email) != '' && !$this->is_valid_email(trim($email))) {

				$label = $this->get_label($field_name);

				$this->set_error($field_name, $this->validator_error('valid_emails', compact('label')));

				return false;
			}
		}

		return true;
	}

	public function valid_base64($field_name, $field_value) {
		if(!preg_match('/[^a-zA-Z0-9\/\+=]/', $field_value))
			return true;

		$label = $this->get_label($field_name);

		$this->set_error($field_name, $this->validator_error('valid_base64', compact('label')));

		return false;
	}

	public function valid_ip($field_name, $field_value) {
		$bad_ip = true;
		$ip_segments = explode('.', $field_value);

		// Always 4 segments needed
		if(sizeof($ip_segments) == 4) {
			// IP can not start with 0
			if($ip_segments[0][0] != '0') {
				$bad_ip = false;
				// Check each segment
				foreach($ip_segments as $segment) {
					// IP segments must be digits and can not be
					// longer than 3 digits or greater then 255
					if($segment == '' || preg_match("/[^0-9]/", $segment) || $segment > 255 || strlen($segment) > 3) {
						$bad_ip = true;
						break;
					}
				}
			}
		}

		if($bad_ip) {
			$label = $this->get_label($field_name);

			$this->set_error($field_name, $this->validator_error('valid_ip', compact('label')));
		}

		return !$bad_ip;
	}

}

/* End of file form_validator.php */
/* Location: ./system/helpers/form_validator.php */